#pragma once
#include "fiber.h"
#include "mutex.h"

template<typename T>
class chan
{
public:
	typedef spinlock_fiber_queue<spinlock_fiber_queue_name::chan_blocking_fiber_queue> 
		blocking_fiber_queue_t;

	chan(const chan &) = delete;
	chan &operator=(const chan&) = delete;

	chan(int capacity = 1024)
		:capacity_(capacity)
	{
		if (capacity_ <= 0)
		{
			capacity_ = 1;
		}
	}
	T recv ()
	{
		auto _fiber = current_fiber;
		if (items_.empty())
		{
		block_fiber:
			recv_blocking_fibers_.push_back (_fiber);
			_fiber->set_status (fiber::status::e_blocking);
			_fiber->yield ();
		}
		if (items_.empty ())
			goto block_fiber;
		T t = std::move (get_item ());
		wakeup_one_fiber (send_blocking_fibers_);
		return std::move(t);
	}
	void send (T &&t)
	{
		auto _fiber = current_fiber;
		if (items_.size() >= capacity_)
		{
		block_fiber:
			send_blocking_fibers_.push_back (_fiber);
			_fiber->set_status (fiber::status::e_blocking);
			_fiber->yield ();
		}
		if (items_.size() >= capacity_)
			goto block_fiber;
		store_item (std::forward<T> (t));
		wakeup_one_fiber (recv_blocking_fibers_);
	}
	void send (T &t)
	{
		auto _fiber = current_fiber;
		if (items_.size () >= capacity_)
		{
		block_fiber:
			send_blocking_fibers_.push_back (_fiber);
			_fiber->set_status (fiber::status::e_blocking);
			_fiber->yield ();
		}
		if (items_.size () >= capacity_)
			goto block_fiber;
		store_item (t);
		wakeup_one_fiber (recv_blocking_fibers_);
	}
private:
	void wakeup_one_fiber (blocking_fiber_queue_t &fibers)
	{
		if (fibers.empty())
			return;
		auto _fiber = fibers.front ();
		fibers.pop_front();
		_fiber->set_status (fiber::status::e_running);
		_fiber->get_scheduler()->append_fiber (_fiber);
	}
	void store_item (T &&t)
	{
		std::lock_guard<spinlock> lock (mtx_);
		items_.push_back (std::move (t));
	}
	void store_item (T &t)
	{
		std::lock_guard<spinlock>lock (mtx_);
		items_.push_back (t);
	}
	T get_item ()
	{
		std::lock_guard<spinlock> lock (mtx_);
		T t = items_.front ();
		items_.pop_front ();
		return std::move(t);
	}
	std::size_t capacity_;

	std::list<T> items_;
	spinlock mtx_;

	blocking_fiber_queue_t send_blocking_fibers_;
	blocking_fiber_queue_t recv_blocking_fibers_;
};